#include <string.h>
// #include "log.h"
#include <string>
#include <map>
#include <vector>
#include <list>
#include "threads.h"

#ifndef __CNATS_HEAD_FILE__
#define __CNATS_HEAD_FILE__

using namespace std;

class Server {
public:
    string host;
    int port;
public:
    Server(string host,int port);
};

enum TYPE_recvedMsgs{
	TYPE_RECVMSG,
	TYPE_RECVREPLY_SUCC,
	TYPE_RECVREPLY_FAIL
	};
struct recvedMsgs{
	int type;
	string subj;
	string reply;
	char *data;
	int dataLen;
	recvedMsgs(bool succ){
		if(succ){
			type = TYPE_RECVREPLY_SUCC;
		}else{
			type = TYPE_RECVREPLY_FAIL;
		}
	}
	recvedMsgs(string subj, string reply, char* data, int len):
		type(TYPE_RECVMSG),
		subj(subj),
		reply(reply),
		data(data),
		dataLen(len)
	{
	}
};

class MsgHandler{
public:
    virtual void execute(recvedMsgs *msg) = 0;
    virtual void asyncErrorCallBack(){}
    virtual void asyncSuccCallBack(){}
};

enum TYPE_msgsToSend {
    TYPE_SUB,
    TYPE_PUB
};

struct msgsToSend{
    int         type;
    string      subj;
    string      reply;
    const char*       data;
    int 		dataLen;
    MsgHandler  *handler;
    msgsToSend(string subj, const char *data, int len,MsgHandler * handler):
        type(TYPE_PUB),
        subj(subj),
        reply(""),
        data(data),
        dataLen(len),
        handler(handler)
    {
    }
    msgsToSend(string subj,string reply, MsgHandler *handler):
        type(TYPE_SUB),
        subj(subj),
        reply(reply),
        data(NULL),
        handler(handler)
    {
    }
};


class cnats{
friend class MsgProcessor;
friend class ReqProcessor;
private:
    vector<Server>              servers;
    map<string, MsgHandler*>    subscribeMap;
    int                         serverCurrent;
    bool                        reconnectFlag;
    int                         socketfd;
    unsigned int                SSID;
    threads                     threadPool;

    sem_t                       SubPubMutex;
    sem_t                       ReqSem;
    list<msgsToSend*>           ReqMessages;
    list<msgsToSend*>           ProcessedMessages;


private:
    void reconnect();
    void issueReconnect();

public:
    cnats();
    static cnats* Connect(string urls);
    bool addServer(string host, int port);
    bool Subscribe(string subj, MsgHandler *handler);
    bool Publish(string subj,
    	const char * data,
    	int len,
    	MsgHandler* handler = NULL
    	);
};

#endif